
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#define WIN32_EXTRA_LEAN

#include <io.h>
#include <glf/core/str.h>
#include <glf/core/inlines.h>

#include <windows.h>

#include <sys/stat.h>

#undef MAX_PATH

namespace glf { 

struct Fs::Impl : public FsImplBase{
	Impl(Fs& fs) :
		FsImplBase(fs) {
	}
	virtual ~Impl() {
	}

	bool RemoveFile(const char* file) {
		return ::DeleteFile(file) != 0;
	}

	bool DoesFileExist(const char* path) {
		return _access(path, 0) != -1;
	}

	virtual void GetCwd(int size, char* buffer) {
		GetCurrentDirectory(size, buffer);
	}

	virtual void SetCwd(const char* cwd) {
		SetCurrentDirectory(cwd);
	}

	bool MkDir(const char* dir, openflag flag) {
		SECURITY_ATTRIBUTES attr;
		attr.nLength = sizeof(SECURITY_ATTRIBUTES);
		attr.lpSecurityDescriptor = NULL;
		attr.bInheritHandle = FALSE;
		return CreateDirectory(dir, &attr) != 0;
	}

	bool RmDir(const char* dir, openflag flag) {
		EmptyDir(dir, flag);
		RemoveDirectory(dir);

		return true;
	}

	bool EmptyDir(const char* dir, openflag flag) {
		
		//char buffer[MAX_PATH];
		stringc buffer = JoinPath(dir, "*");
	
		//JoinPath(buffer, dir, "*");
		
		WIN32_FIND_DATA info;
		HANDLE hp = FindFirstFile(buffer.c_str(), &info);
		
		if(hp != INVALID_HANDLE_VALUE) {
			do {
				const char* filename = info.cFileName;

				if(strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) {
					continue;
				}

				if((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)== FILE_ATTRIBUTE_DIRECTORY) {
					buffer = JoinPath(dir, filename);
					EmptyDir(buffer.c_str(), flag);
					RemoveDirectory(buffer.c_str());
				} else {
					buffer = JoinPath(dir, filename);
					DeleteFile(buffer.c_str());
				}

			} while(FindNextFile(hp, &info)); 
			
			FindClose(hp);
		}

		return true;
	}

	bool Init(const char* dataRoot) {
		return true;
	}
};

namespace fs {

struct DirHandle::Impl {
	DirHandle& mDirHandle;

	long mHandle;
	openflag mFlag;

	stringc mPath;

	Impl(DirHandle& handle) :
		mDirHandle(handle) {
		mHandle = 0;
	}
	~Impl() {
		Close();
	}

	void Fill(_finddata_t& info, FileInfo& fi) {
		fi.filename = info.name;
		fi.fullpath = JoinPath(mPath, fi.filename);
		
		//fi.flag = 0;
		fi.isDir = TestFlag(info.attrib, _A_SUBDIR);
		fi.isHidden = TestFlag(info.attrib, _A_HIDDEN);
		fi.size = info.size;
		fi.timeCreated = info.time_create;
		fi.timeLastModified = info.time_write;
		fi.timeLastAccessed = info.time_access;
	}
	
	bool _FindFirst(openflag flag, FileInfo& fi) {
		mFlag = flag;

		bool isOk = _FindNext(fi);
		
		if(!isOk) {
			Close();
		}

		return isOk;
	}
	
	bool FindFirst(const char* path, openflag flag, FileInfo& fi) {
		Close();

		if(path[0] == '/') {
			mPath = JoinPath(".", path);
		} else {
			mPath = path;
		}

		return _FindFirst(flag, fi);
	}

	bool _FindNext(FileInfo& fi) {
		_finddata_t info;
		if(mHandle == 0) {
			//char buffer[MAX_PATH];
			stringc buffer = JoinPath(mPath, "*");
			mHandle = _findfirst(buffer.c_str(), &info);
			if(mHandle <= 0) {
				mDirHandle.mLastError = ERROR_NOEXISTS;
				return false;
			}
		} else if(_findnext(mHandle, &info) != 0) {
			mDirHandle.mLastError = ERROR_COUNT;
			return false;
		}

		Fill(info, fi);
		return true;

	}

	bool FindNext(FileInfo& fi) {

		if(mHandle) {
			return _FindNext(fi);
		}

		mDirHandle.mLastError = ERROR_NOTOPENED;
		return false;
	}

	void Close() {
		if(mHandle) {
			mDirHandle.mLastError = ERROR_NONE;
			_findclose(mHandle);
			mHandle = 0;
		}
	}
};

} //namespace fs
} //namespace glf


#undef CopyFile
